完整指令的执行——数据路径实现
AP SHANTHI博士
本模块的目标是使用 MIPS 架构作为案例研究,讨论如何在处理器中执行指令和数据路径实现。
MIPS架构的特点首先总结如下:
• 32 位字节地址对齐——MIPS 使用32 个对齐的双地址。
• 仅加载/存储位移寻址——它是加载/存储ISA 或寄存器/寄存器ISA,其中只有加载和存储指令使用内存操作数。所有其他指令仅使用寄存器操作数。内存操作数采用的寻址方式是位移寻址,必须在基址寄存器内容上加上位移才能得到有效地址。
• 标准数据类型——ISA 支持所有标准数据类型。
• 32 个 GPR – 有 32 个通用寄存器,寄存器 R0 始终为 0。
• 32 个 FPR – 有 32 个浮点寄存器。
• FP 状态寄存器——有浮点状态寄存器。
• 无条件代码——MIPS 架构不支持条件代码。
• 寻址模式——支持的寻址模式是立即、位移和寄存器模式(仅用于 ALU)
3 种固定长度格式——支持 3 种 32 位指令格式。它们如下图 8.1 所示。
我们将检查 MIPS 实现的一个简单子集,该子集显示了实现的大多数方面。考虑的指令是:
内存引用指令加载字 (lw) 和存储字 (sw)
算术逻辑指令 add、sub、and、or 和 slt
指令分支等于(beq)和跳转(j)最后要考虑。
该子集不包括所有整数指令(例如,缺少移位、乘法和除法),也不包括任何浮点指令。但是,将说明用于创建数据路径和设计控件的关键原则。其余指令的实现类似。我们之前看到的关键设计原则可以通过查看实现来说明,例如通用准则,“使常见案例快速”和“简单有利于规律性”。此外,用于实现 MIPS 子集的大多数概念与用于构建从高性能服务器到通用微处理器再到嵌入式处理器的广泛计算机的基本思想相同。
当我们查看任何处理器的指令周期时,它应该涉及以下操作:
从内存中获取指令
解码指令
获取操作数
执行指令
写出结果
我们将针对指令子集详细查看这些步骤中的每一个。实现这些指令所需要做的大部分工作都是相同的,与具体的指令类别无关。对于每条指令,取指令和解码的前两个步骤是相同的:
将程序计数器 (PC) 发送到包含代码的程序存储器并获取指令
使用指令中的寄存器说明符字段读取一个或两个寄存器。对于加载字指令,我们只需要读取一个寄存器,但大多数其他指令需要我们读取两个寄存器。由于 MIPS 使用固定长度格式,并且寄存器说明符位于同一位置,因此无论指令如何,都可以读取寄存器。
在这两个步骤之后,完成指令所需的动作取决于指令的类型。对于算术/逻辑、内存引用和分支这三个指令类中的每一个,操作基本相同。即使在不同的教学课程中也有一些相似之处。例如,除跳转外的所有指令类在读取寄存器后都使用算术和逻辑单元ALU。加载/存储内存引用指令使用 ALU 进行有效地址计算,使用算术和逻辑指令进行运算执行,并使用分支进行条件评估,这里是比较。正如我们所见,指令集的简单性和规律性通过使许多指令类的执行相似而简化了实现。使用 ALU 后,完成各种教学课程所需的操作各不相同。内存引用指令将需要访问内存。对于加载指令,必须执行内存读取。对于存储指令,必须执行内存写入。算术/逻辑指令必须将 ALU 中的数据写回寄存器。加载指令还必须将从内存中获取的数据写入寄存器。最后,对于分支指令,我们可能需要根据比较结果更改下一条指令地址。如果条件比较失败,PC 应加 4 以得到下一条指令的地址。如果条件为真,则必须在 PC 中更新新地址。下面的图 8.2 给出了 CPU 的概览。内存引用指令将需要访问内存。对于加载指令,必须执行内存读取。对于存储指令,必须执行内存写入。算术/逻辑指令必须将 ALU 中的数据写回寄存器。加载指令还必须将从内存中获取的数据写入寄存器。最后,对于分支指令,我们可能需要根据比较结果更改下一条指令地址。如果条件比较失败,PC 应加 4 以得到下一条指令的地址。如果条件为真,则必须在 PC 中更新新地址。下面的图 8.2 给出了 CPU 的概览。内存引用指令将需要访问内存。对于加载指令,必须执行内存读取。对于存储指令,必须执行内存写入。算术/逻辑指令必须将 ALU 中的数据写回寄存器。加载指令还必须将从内存中获取的数据写入寄存器。最后,对于分支指令,我们可能需要根据比较结果更改下一条指令地址。如果条件比较失败,PC 应加 4 以得到下一条指令的地址。如果条件为真,则必须在 PC 中更新新地址。下面的图 8.2 给出了 CPU 的概览。算术/逻辑指令必须将 ALU 中的数据写回寄存器。加载指令还必须将从内存中获取的数据写入寄存器。最后,对于分支指令,我们可能需要根据比较结果更改下一条指令地址。如果条件比较失败,PC 应加 4 以得到下一条指令的地址。如果条件为真,则必须在 PC 中更新新地址。下面的图 8.2 给出了 CPU 的概览。算术/逻辑指令必须将 ALU 中的数据写回寄存器。加载指令还必须将从内存中获取的数据写入寄存器。最后,对于分支指令,我们可能需要根据比较结果更改下一条指令地址。如果条件比较失败,PC 应加 4 以得到下一条指令的地址。如果条件为真,则必须在 PC 中更新新地址。下面的图 8.2 给出了 CPU 的概览。PC 应该增加 4 以获得下一条指令的地址。如果条件为真,则必须在 PC 中更新新地址。下面的图 8.2 给出了 CPU 的概览。PC 应该增加 4 以获得下一条指令的地址。如果条件为真,则必须在 PC 中更新新地址。下面的图 8.2 给出了 CPU 的概览。
然而,只要我们有两种可能的输入,我们就不能将电线连接在一起。
我们必须使用多路复用器,如下图 8.3 所示。
我们还需要包括必要的控制信号。下面的图 8.4 显示了数据路径以及主要功能单元的控制线。控制单元接收指令作为输入,并决定如何为功能单元和两个多路复用器设置控制线。第三个多路复用器决定是否将 PC+4 或分支目标地址写入 PC,它是根据 ALU 的零输出设置的,用于在相等指令上执行分支的比较。MIPS 指令集的规律性和简单性意味着可以通过一个简单的解码过程来确定如何设置控制线。
Just to give a brief section on the logic design basics, all of you know that information is encoded in binary as low voltage = 0, high voltage = 1 and there is one wire per bit. Multi-bit data are encoded on multi-wire buses. The combinational elements operate on data and the output is a function of input. In the case of state (sequential) elements, they store information and the output is a function of both inputs and the stored data, that is, the previous inputs. Examples of combinational elements are AND-gates, XOR-gates, etc. An example of a sequential element is a register that stores data in a circuit. It uses a clock signal to determine when to update the stored value and is edge-triggered.
现在,我们将讨论数据路径的实现。数据路径由处理 CPU 中数据和地址的元素组成——寄存器、ALU、多路复用器、存储器等。我们将逐步构建 MIPS 数据路径。我们将构建基本模型并不断完善它。
CPU 执行取指令操作的部分如图 8.5 所示。
如前所述,PC 用于寻址指令存储器以获取指令。同时,PC的值也被送入加法器单元加4,这样PC+4,即MIPS中下一条指令的地址,被写入PC,从而为下一条指令做好准备拿来。
下一步是指令解码和操作数提取。在 MIPS 的情况下,解码完成,同时读取寄存器文件。处理器的 32 个通用寄存器存储在称为寄存器文件的结构中。寄存器文件是寄存器的集合,可以通过指定文件中的寄存器编号来读取或写入其中的任何寄存器。
R 格式指令具有三个寄存器操作数,我们需要从寄存器文件中读取两个数据字,并为每条指令将一个数据字写入寄存器文件。对于要从寄存器读取的每个数据字,我们需要指定要读取的寄存器编号的寄存器文件的输入和寄存器文件的输出,该输出将携带从寄存器读取的值。要写入数据字,我们需要两个输入——一个指定要写入的寄存器编号,另一个提供要写入寄存器的数据。5 位寄存器说明符指示要使用的 32 个寄存器之一。
The register file always outputs the contents of whatever register numbers are on the Read register inputs. Writes, however, are controlled by the write control signal, which must be asserted for a write to occur at the clock edge. Thus, we need a total of four inputs (three for register numbers and one for data) and two outputs (both for data), as shown in Figure 8.6. The register number inputs are 5 bits wide to specify one of 32 registers, whereas the data input and two data output buses are each 32 bits wide.
After the two register contents are read, the next step is to pass on these two data to the ALU and perform the required operation, as decided by the control unit and the control signals. It might be an add, subtract or any other type of operation, depending on the opcode. Thus the ALU takes two 32-bit inputs and produces a 32-bit result, as well as a 1-bit signal if the result is 0. The control signals will be discussed in the next module. For now, we wil assume that the appropriate control signals are somehow generated.
The same arithmetic or logical operation with an immediate operand and a register operand, uses the I-type of instruction format. Here, Rs forms one of the source operands and the immediate component forms the second operand. These two will have to be fed to the ALU. Before that, the 16-bit immediate operand is sign extended to form a 32-bit operand. This sign extension is done by the sign extension unit.
接下来我们将考虑 MIPS 加载字和存储字指令,它们具有一般形式 lw $t1,offset_value($t2) 或 sw $t1,offset_value ($t2)。这些指令通过将基址寄存器 $t2 添加到指令中包含的 16 位带符号偏移字段来计算内存地址。如果指令是存储指令,则要存储的值也必须从它驻留在 $t1 中的寄存器文件中读取。如果指令是加载,则从内存中读取的值必须写入指定寄存器中的寄存器文件,即$t1。因此,我们将需要寄存器文件和 ALU。此外,符号扩展单元会将指令中的 16 位偏移字段符号扩展为 32 位有符号值。加载和存储操作的下一个操作是数据存储器访问。必须为加载指令读取数据存储器单元,必须为存储指令写入数据存储器单元;因此,它具有读和写控制信号、地址输入以及要写入存储器的数据的输入。上面的图 8.7 说明了这一切。
相等指令上的分支具有三个操作数,两个寄存器用于比较 相等,以及用于计算分支目标地址的 16 位偏移量,相对于分支指令地址。它的形式是 beq $t1, $t2, offset。为了实现这条指令,我们必须通过将指令的符号扩展偏移字段添加到 PC 来计算分支目标地址。指令集架构指定分支地址计算的基址是分支之后的指令的地址。由于我们已经在取指数据路径中计算了下一条指令的地址PC + 4,因此很容易使用该值作为计算分支目标地址的基数。此外,由于字边界的 2 个 LSB 为零,并且分支目标地址必须从字边界开始,因此偏移字段左移 2 位。除了计算分支目标地址,我们还必须确定下一条指令是顺序跟随的指令还是分支目标地址处的指令。这取决于正在评估的条件。当条件为真(即操作数相等)时,分支目标地址成为新的PC,我们说分支被采用。如果操作数不相等,则递增的 PC 应该替换当前的 PC(就像任何其他正常指令一样);在这种情况下,我们说分支没有被采用。递增的 PC 应该替换当前的 PC(就像任何其他正常指令一样);在这种情况下,我们说分支没有被采用。递增的 PC 应该替换当前的 PC(就像任何其他正常指令一样);在这种情况下,我们说分支没有被采用。
因此,分支数据路径必须执行两个操作:计算分支目标地址和比较寄存器内容。如图 8.8 所示。为了计算分支目标地址,分支数据路径包括一个符号扩展单元和一个加法器。为了执行比较,我们需要使用寄存器文件来提供两个寄存器操作数。由于 ALU 提供指示结果是否为 0 的输出信号,我们可以将两个寄存器操作数发送到 ALU,并在控制集上进行减法运算。如果 ALU 单元输出的零信号被断言,我们就知道这两个值是相等的。尽管零输出总是在结果为 0 时发出信号,但我们将仅使用它来实现分支的相等测试。稍后,我们将具体展示如何连接 ALU 的控制信号以用于数据通路。
现在,我们已经检查了各个指令类所需的数据路径组件,我们可以将它们组合成单个数据路径并添加控件以完成实现。组合数据路径如下图 8.9 所示。
最简单的数据路径可能会尝试在一个时钟周期内执行所有指令。这意味着每条指令不能多次使用任何数据路径资源,因此任何需要多次使用的元素都必须复制。因此,我们需要一个用于指令的存储器与用于数据的存储器分开。虽然一些功能单元需要复制,但许多元素可以由不同的指令流共享。为了在两个不同的指令类之间共享数据路径元素,我们可能需要允许多个连接到元素的输入,使用多路复用器和控制信号在多个输入中进行选择。在添加多路复用器时,我们应该注意,虽然算术/逻辑(R 型)指令和内存相关指令数据路径的操作非常相似,但还是有一些关键的区别。
R 型指令使用来自寄存器文件的两个寄存器操作数。内存指令也使用 ALU 进行地址计算,但第二个输入是指令的符号扩展 16 位偏移字段。
对于 R 型指令,存储到目标寄存器中的值来自 ALU,而对于加载,数据来自内存。
要创建具有公共寄存器文件和 ALU 的数据路径,我们必须支持第二个 ALU 输入的两个不同来源,以及存储到寄存器文件中的数据的两个不同来源。因此,一个多路复用器需要放置在 ALU 输入端,另一个放置在寄存器文件的数据输入端,如图 8.10 所示。
我们已经讨论了单个指令——算术/逻辑、内存相关和分支。现在,我们可以通过添加用于指令获取的数据路径、来自 R 类型和内存指令的数据路径以及用于分支的数据路径,将所有部分组合起来,为 MIPS 架构创建一个简单的数据路径。下图显示了我们通过组合单独的部分获得的数据路径。分支指令使用主 ALU 来比较寄存器操作数,因此我们必须保留前面显示的加法器来计算分支目标地址。需要一个额外的多路复用器来选择顺序跟随的指令地址 PC + 4 或要写入 PC 的分支目标地址。
总而言之,我们以 MIPS 作为案例研究了执行完整指令的步骤。我们为算术/逻辑指令、加载/存储指令和分支指令逐步构建了数据路径。跳转指令到数据路径的实现和控制路径的实现将在下一个模块中讨论。
网页链接/支持材料
计算机组织与设计——硬件/软件接口,David A. Patterson 和 John L. Hennessy,第 4 版,Morgan Kaufmann,Elsevier,2009 年。
计算机组织,Carl Hamacher、Zvonko Vranesic 和 Safwat Zaky,第 5 版,McGraw-Hill 高等教育,2011 年。